/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

//use double precision for triangle intersection code (slower but higher precision)
//#define DOUBLE_PRECISION_TRIANGLES

#include "skar_rayengine_packet_BriteNativeRayPacketEngine.H"
#include "Scene.H"
#ifdef DOUBLE_PRECISION_TRIANGLES
#include "TriangleDP.H"
#else
#include "Triangle.H"
#endif
#include "Parallelogram.H"
#include "Disk.H"
#include "Sphere.H"
#include "BriteDefs.H"
#include <float.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    initInstance
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_initInstance
  (JNIEnv *, jclass)
{
  Scene *scene = new Scene();
  return (jlong)scene;
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    addTriangleNative
 * Signature: (JIFFFFFFFFF)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_addTriangleNative
  (JNIEnv *, jclass, jlong thisptr, jint geomId, jfloat v0x, jfloat v0y, jfloat v0z, jfloat v1x, jfloat v1y, jfloat v1z, jfloat v2x, jfloat v2y, jfloat v2z)
{
  Scene *scene = (Scene *)thisptr;
  PoolAllocator *geomPool = scene->getGeometryObjectPool();
#ifdef DOUBLE_PRECISION_TRIANGLES
  TriangleDP *t = TriangleDP::poolAlloc(geomPool);
  t->set(v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z,geomId);
#else
  Triangle *t = Triangle::poolAlloc(geomPool);
  t->set(v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z,geomId);
#endif
  scene->addGeometryObject(t);
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    addRectangleNative
 * Signature: (JIFFFFFFFFFFFF)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_addRectangleNative
  (JNIEnv *, jclass, jlong thisptr, jint geomId, jfloat v0x, jfloat v0y, jfloat v0z, jfloat v1x, jfloat v1y, jfloat v1z, jfloat v2x, jfloat v2y, jfloat v2z, jfloat v3x, jfloat v3y, jfloat v3z)
{
  Scene *scene = (Scene *)thisptr;
  PoolAllocator *geomPool = scene->getGeometryObjectPool();
  Parallelogram *p = Parallelogram::poolAlloc(geomPool);
  p->set(v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z,v3x,v3y,v3z,geomId);
  scene->addGeometryObject(p);
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    addDiskNative
 * Signature: (JIFFFFFFFFFF)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_addDiskNative
  (JNIEnv *, jclass, jlong thisptr, jint geomId, jfloat centerX, jfloat centerY, jfloat centerZ, 
  jfloat normalX, jfloat normalY, jfloat normalZ, jfloat uAxisX, jfloat uAxisY, jfloat uAxisZ, jfloat radius)
{
  Scene *scene = (Scene *)thisptr;
  PoolAllocator *geomPool = scene->getGeometryObjectPool();
  Disk *d = Disk::poolAlloc(geomPool);
  d->set(centerX,centerY,centerZ,normalX,normalY,normalZ,uAxisX,uAxisY,uAxisZ,radius,geomId);
  scene->addGeometryObject(d);
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    addSphereNative
 * Signature: (JIFFFFFFFFFF)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_addSphereNative
  (JNIEnv *, jclass, jlong thisptr, jint geomId, jfloat centerX, jfloat centerY, jfloat centerZ, 
  jfloat uAxisX, jfloat uAxisY, jfloat uAxisZ, jfloat wAxisX, jfloat wAxisY, jfloat wAxisZ, jfloat radius)
{
  Scene *scene = (Scene *)thisptr;
  PoolAllocator *geomPool = scene->getGeometryObjectPool();
  Sphere *s = Sphere::poolAlloc(geomPool);
  s->set(centerX,centerY,centerZ,uAxisX,uAxisY,uAxisZ,wAxisX,wAxisY,wAxisZ,radius,geomId);
  scene->addGeometryObject(s);
}


/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    buildAccelerationStructure
 * Signature: (JZ)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_buildAccelerationStructure
  (JNIEnv *, jclass, jlong thisptr, jboolean printStats)
{
  Scene *scene = (Scene *)thisptr;
//_asm int 3; //force debugger breakpoint
//  scene->buildFlatList();
//  scene->buildNaiveMiddleSplit(5);
  //building the tree deletes the uncompiled geometry automatically
  scene->buildGreedySplit();
  if (printStats) scene->printTreeStats();
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    deleteAll
 * Signature: (J)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_deleteAll
  (JNIEnv *, jclass, jlong thisptr)
{
  Scene *scene = (Scene *)thisptr;
  scene->deleteAll();
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    findFirstIntersectionNative
 * Signature: (JJDDDDDDDD)I
 */
JNIEXPORT jint JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_findFirstIntersectionNative
  (JNIEnv *env, jclass, jlong thisptr, jlong bufferPtr, jdouble originX, jdouble originY, jdouble originZ, jdouble dirX, jdouble dirY, jdouble dirZ, jdouble tStart, jdouble tEnd)
{
  double TMAX = 0.1*FLT_MAX;
//  SSEInexact();
  Scene *scene = (Scene *)thisptr;
  Ray ray;
  //check normalization of ray direction because Brite requires normalized ray directions
  double dirLength2 = dirX*dirX + dirY*dirY + dirZ*dirZ;
  ray.setOrigin((float)originX,(float)originY,(float)originZ);
  //prevent overflow for unbounded rays (start or end of ray is unbounded 
  tStart = (tStart<-TMAX)?-TMAX:tStart;
  tEnd = (tEnd > TMAX)?TMAX:tEnd;
  ray.setTParameters((float) (tStart), (float)(tEnd));
  ray.setDirection((float)(dirX),(float)(dirY),(float)(dirZ));
  IntersectionRecord hitRec;
  hitRec.primitiveId = -1;
  if (fabs(dirLength2 - 1.0) > 1e-4) {
    jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
    if (newExcCls == 0) { /* Unable to find the new exception class, give up. */
	  BRITE_DEBUGBREAK; //force debugger breakpoint
      return 0x7FFFFFFF; //return max int, should cause an exception
    }
    env->ThrowNew(newExcCls, "Brite can only handled normalized rays");
  }
  scene->intersect(ray,&hitRec);
  if (hitRec.primitiveId >= 0) {
    float *buf = (float *)bufferPtr;
    buf[0] = hitRec.t;
    buf[1] = hitRec.u;
    buf[2] = hitRec.v;
  }
  return hitRec.primitiveId;
}

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    findFirstIntersectionBufferNative
 * Signature: (JJ)I
 */
JNIEXPORT jint JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_findFirstIntersectionBufferNative
  (JNIEnv *env, jclass, jlong thisPtr, jlong bufferPtr)
{
  float TMAX = 0.1*FLT_MAX;
  //  SSEInexact();
  Scene *scene = (Scene *)thisPtr;
  Ray ray;
  double *raybuf = (double *)bufferPtr;
  double originX = raybuf[0];
  double originY = raybuf[1];
  double originZ = raybuf[2];
  double dirX = raybuf[3];
  double dirY = raybuf[4];
  double dirZ = raybuf[5];
  double tStart = raybuf[6];
  double tEnd = raybuf[7];
  //check normalization of ray direction because Brite requires normalized ray directions
  double dirLength2 = dirX*dirX + dirY*dirY + dirZ*dirZ;
  ray.setOrigin((float)originX,(float)originY,(float)originZ);
  //prevent overflow for unbounded rays (start or end of ray is unbounded 
  tStart = (tStart<-TMAX)?-TMAX:tStart;
  tEnd = (tEnd > TMAX)?TMAX:tEnd;
  ray.setTParameters((float) (tStart), (float)(tEnd));
  ray.setDirection((float)(dirX),(float)(dirY),(float)(dirZ));
  IntersectionRecord hitRec;
  hitRec.primitiveId = -1;
  if (fabs(dirLength2 - 1.0) > 1e-4) {
    jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
    if (newExcCls == 0) { /* Unable to find the new exception class, give up. */
	  BRITE_DEBUGBREAK; //force debugger breakpoint
      return 0x7FFFFFFF; //return max int, should cause an exception
    }
    env->ThrowNew(newExcCls, "Brite can only handled normalized rays");
  }
  scene->intersect(ray,&hitRec);
  if (hitRec.primitiveId >= 0) {
    float *buf = (float *)bufferPtr;
    buf[0] = hitRec.t;
    buf[1] = hitRec.u;
    buf[2] = hitRec.v;
  }
  return hitRec.primitiveId;
}



// Newer method added to pass the ids on the buffer

/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    findFirstIntersectionBufferNative_New
 * Signature: (JJ)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_findFirstIntersectionBufferNative_1New
  (JNIEnv *, jclass, jlong thisPtr, jlong bufferPtr)
{
  float TMAX = 0.1*FLT_MAX;
  Scene *scene = (Scene *)thisPtr;
  int index;                             // index for the current ray being traced.....,trace each ray along the structure and write the results in the  
  int location = 20;
  int temp;                              // buffer as well as get the primitive index
  double *raybuf = (double *)bufferPtr;  // double view for the writing the t u and v values....   
  float *buf = (float *)bufferPtr;
  int numOfRays = (int)raybuf[0];
  // printf(" numOfRays %d \n",numOfRays);                 // debugging code, finding the number  of rays
  Ray ray[4];
  double origX,origY,origZ,dirX,dirY,dirZ,tStart,tEnd,dirLength2; 
  IntersectionRecord hitRec[4];
  for(index = 0 ; index < numOfRays ; index++)  //ray specific part of the code
     {
     temp = 8 * index;
     origX = raybuf[temp + 1];
     origY = raybuf[temp + 2];
     origZ = raybuf[temp + 3];
     dirX    = raybuf[temp + 4];
     dirY    = raybuf[temp + 5];
     dirZ    = raybuf[temp + 6];
     tStart  = raybuf[temp + 7];
     tEnd    = raybuf[temp + 8];
     dirLength2 = dirX*dirX + dirY*dirY + dirZ*dirZ;
     ray[index].setOrigin((float)origX,(float)origY,(float)origZ);   
     tStart = (tStart<-TMAX)?-TMAX:tStart;
     tEnd = (tEnd > TMAX)?TMAX:tEnd;
     ray[index].setTParameters((float) (tStart), (float)(tEnd));
     ray[index].setDirection((float)(dirX),(float)(dirY),(float)(dirZ));
     // printf(" reading  ray %d \n",index);
	 // printf(" origin %g %g %g \n",origX,origY,origZ);  // debugging the c code and printing the values of the ray parameters)
     //  printf(" direction %g %g %g \n",dirX,dirY,dirZ);  
	 
	 
	 
	   //if (fabs(dirLength2 - 1.0) > 1e-4)//checking for the errors and the illegal arguements the way , it has been done in the upper routine.  
	   // {
       // jclass newExcCls = env->FindClass("java/lang/IllegalArgumentException");
       // if (newExcCls == 0) 
	   //  { /* Unable to find the new exception class, give up. */
       //  _asm int 3; //force debugger breakpoint
       // return 0x7FFFFFFF; //return max int, should cause an exception                    NOT CHECKING FOR THE NORMALIZATION OF THE RAY IN THE C CODE
	   //  }                                                                                ASSUME THAT NORMALIZED RAYS ARE PASSED FROM THE TOP	 
       //env->ThrowNew(newExcCls, "Brite can only handled normalized rays");
	   // }
         // pass the id on the buffer and -1 would be passed depending upon the intersection found or not  
     }
       
      // Checking the signs and then calling the intersect method  
      int done[4];
      int i,j,k;
	  
	  for( i = 0 ; i < 4 ; i++)done[i] = -1; //none of the rays are done right now
      Ray rayTemp[4];                        //temporary ray array created to be passed
	  for(int temp2 = numOfRays ; temp2 < 4 ; temp2++)done[temp2] = 0; //if numOfRays < 4 , then asssume that the last ones are already done....
	  int count[4];for(int p = 0; p< 4; p++)count[p] = -1;  // count will hold the indices of the rays currently beingintersected, hence initially all are -1
	  bool finished = false;  // not finished tracing the rays
	  while(!finished)
	     {
	      i = 0;j = 0;k = 0;    
	      while(done[i]!=(-1))i++;                              // get the first ray not yet traced
	      rayTemp[j++] = ray[i] ; done[i] = 0 ;count[k++] = i;  // add the ray , this ray is done, store the index of the ray
          for(int temp1 = i + 1; temp1 < 4 ; temp1++)// find the next rays that can be traced alongwith this, note:we need to check if the ray has already been done 
			  if((ray[i].dirX()*ray[temp1].dirX()>0) && (ray[i].dirY()*ray[temp1].dirY()>0) && (ray[i].dirZ()*ray[temp1].dirZ()>0) && (done[temp1] == (-1)) )	     // traced 
			  {
				  rayTemp[j++] = ray[temp1] ; done[temp1] = 0 ; count[k++] = temp1 ; // if found such a ray, add that to tempRay,this ray is also done,add the index
			  }                                                                      // temp1 of this ray to count array 
		  //printf(" rays intersected together %d \n" , j); // number of rays found intersection together
	      buf[location++] = (float)j;                       // line added during coherence measurement read and add j on the buffer, use it later.......
	      scene->intersect(j,rayTemp,hitRec); //intersect method, j holds the number of rays to be traced simaltaneously
	      k = 0; //read the count array for indices of the ray that were traced                             
		  for(index = count[k] ; k < j ; )// get index from count array, and put all the values on the buffer, while you have not written the data for 
         	 {                               // exactly j rays, (note: j number of rays were initially passed)
	          //if(hitRec[k].primitiveId >= 0) 
		    // {                                          // temporary changes done only for debugging purposes.
               temp = (4*index);						  /* (float)(4*index); <-- the cast to float seems not to make sense (edgar) */
               buf[temp + 0] = hitRec[k].t;
               buf[temp + 1] = hitRec[k].u;
               buf[temp + 2] = hitRec[k].v;
	      //   }
              buf[temp + 3] = static_cast<float>(hitRec[k].primitiveId);				// Java performs the inverse cast: int id = (int)float_val;
		  //     printf(" writing record %d \n",index);
		  //	  printf("  t , u , v and p_Id %g %g %g %d \n" , hitRec[k].t , hitRec[k].u , hitRec[k].v , hitRec[k].primitiveId );         // debugging printing the return values 
			  k++ ; index = count[k] ;                                                  // index being updated after the dubugging code...
		     }
		  for(int p = 0; p<4 ;p++){ count[p] = -1 ;}                                    // refresh the count array for the next loop
		  finished = ((done[0]==0) && (done[1]==0) && (done[2]==0) & (done[3]==0));     // you are finished when all the values in the done array are 0 
	   }                                                                                // and hence you would not enter the while loop again 
          
	  // fflush(stdout);
}
	  
	  
	  
	  // for(i = 0 ; i < 4 ; i++)  
      // for(j = (i + 1) ; j < 4 ; j++)
      // canCombine[i][j] = (  (ray[i].dirX*ray[j].dirX>0) && (ray[i].dirY*ray[j].dirY>0) && (ray[i].dirZ*ray[j].dirZ>0) )
     
     
	 /*
	 if(canCombine[0][1] && canCombine[1][2] && canCombine[2][3]) // all 4 can be combined	 			 
	 {
		 scene->intersect(ray, &hitRec);		 
		 		 
	 }	                                                          //                         
	 else if(canCombine[0][1] && canCombine[1][2])// 0,1,2 together, 3 out 	 
	 {		 
         
		 
	 
	 }
	 else if(canCombine[0][1] && canCombine[1][3])// 0,1,3 together, 2 out
	 {

	 
	 }		 
	 else if(canCombine[0][2] && canCombine[2][3])// 0,2,3 together, 1 out
	 {

	 
	 }		 
	 else if(canCombine[1][2] && canCombine[2][3])// 1,2,3 together, 0 out
	 {

	 
	 }		 
	 else if(canCombine[0][1] && canCombine[2][3])// 0,1 together, 2,3 together
	 {

	 
	 }		 
	 else if(canCombine[0][2] && canCombine[1][3])// 0,2 together, 1,3 together
	 {

	 
	 }		 
	 else if(canCombine[0][3] && canCombine[1][2])// 0,3 together, 1,2 together
	 {

	 
	 }		 
	 else if(canCombine[0][1])// 0,1 together, 2 & 3 out 	 
	 {


	 }
     else if(canCombine[0][2])// 0,2 together, 1 & 3 out 	 
	 {


	 }
     else if(canCombine[0][3])// 0,3 together, 1 & 2 out 	 
	 {


	 }
     else if(canCombine[1][2])// 1,2 together, 0 & 3 out 	 
	 {


	 }
     else if(canCombine[1][3])// 1,3 together, 0 & 2 out 	 
	 {


	 }
     else if(canCombine[2][3])// 2,3 together, 0 & 1 out 	 
	 {


	 }
	 else // all individually traced
	 {
		 for(int k = 0 ; k < 4 ; k++)scene->intersect(ray[i], &hitRec[i]);		 
		 
	 }	 
		 
	 */	 
	 //Checking the values, and, then write the values on the buffer the same way as the values were written earlier 
     // Rays not needed to write on the buffer 
     
     






/*
 * Class:     skar_rayengine_packet_BriteNativeRayPacketEngine
 * Method:    getBoundingBoxNative
 * Signature: (JJ)V
 */
JNIEXPORT void JNICALL Java_skar_rayengine_packet_BriteNativeRayPacketEngine_getBoundingBoxNative
  (JNIEnv *, jclass, jlong thisptr, jlong bufferPtr)
{
  Scene *scene = (Scene *)thisptr;
  float *buf = (float *)bufferPtr;
  AxisAlignedBox bounds = scene->getBoundingBox();
  buf[0] = bounds.min[0];
  buf[1] = bounds.min[1];
  buf[2] = bounds.min[2];
  buf[3] = bounds.max[0];
  buf[4] = bounds.max[1];
  buf[5] = bounds.max[2];
}

#ifdef __cplusplus
}
#endif

